#pragma once
#include "elisa/elisa.h"
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <gaol/gaol>

#include "Modeler.h"

#ifdef ELISA_PERFORMER
#include "elisa_chrono.h"
#endif

using namespace std;
using namespace elisa;

/* Problem: enfant1

Birch, B: "Noncongruence Subgroups, Covers and Drawings",
In "The Grothendieck Theory of Dessins d'Enfants", editor: Schneps, L.,
London Mathematical Society Lecture Series 200,
Cambridge University Press, pages 25-46, 1994. 
*/

class Enfant1 : public Modeler
{
private:
	Model _model;

	RealVar x1;
	RealVar x2;
	RealVar x3;
	RealVar x4;
	RealVar x5;
	RealVar x6;
	RealVar x7;
	RealVar x8;
	RealConstraint c1;
	RealConstraint c2;
	RealConstraint c3;
	RealConstraint c4;
	RealConstraint c5;
	RealConstraint c6;
	RealConstraint c7;
	RealConstraint c8;

public:
	Enfant1()
		: x1(Interval(-1000,1000), "x1"),
		  x2(Interval(-1000,1000), "x2"),
		  x3(Interval(-1000,1000), "x3"),
		  x4(Interval(-1000,1000), "x4"),
		  x5(Interval(-1000,1000), "x5"),
		  x6(Interval(-1000,1000), "x6"),
		  x7(Interval(-1000,1000), "x7"),
		  x8(Interval(-1000,1000), "x8"),
		  c1(x8 == 1),
		  c2(x2*(6*x1+14*x6) + x1*(10*x4*x6+48*x5) + x3*(8*x7*x1-162*pow(x1,2)+16*x5) == 0),
		  c3(x3*(15*x1+27*x6) + x4*(30*x5+18*x1*x7 - 162*pow(x1,2)) +
		     x2*(24*x7-312*x1) + x1*(24*x5+84*x6) == 0),
		  c4(240*x1 + 64*x4 == 420 + 112*x7),
		  c5(x1*(180 - 284*x4 - 162*x1) + 55*x3 + x7*(60*x4 + 50*x1) + 70*x5 + 260*x6 - 112*x2 == 0),
		  c6(-990*x1 + 336*x7 + 90*x6 + 78*x4 - 90*x3 == 0),
		  c7(x5*(4*x4*x1 + 6*x2) + x2*(2*x7*x1 - 162*pow(x1,2)) + 3*x6*x3*x1 == 0),
		  c8(x4*(28*x1 + 44*x6) + 36*x2*x6 + 192*x5 + 128*x7*x1 + x3*(40*x7-300*x1) - 648*pow(x1,2) == 0),
		  _model()
	{
	}

	elisa::Model & model()
	{
		_model = c1 && c2 && c3 && c4 && c5 && c6 && c7 && c8;
		return _model;
	}

	void print()
	{
		cout << '\t' << x1.getName() << " = " << x1.domain() << endl;
		cout << '\t' << x2.getName() << " = " << x2.domain() << endl;
		cout << '\t' << x3.getName() << " = " << x3.domain() << endl;
		cout << '\t' << x4.getName() << " = " << x4.domain() << endl;
		cout << '\t' << x5.getName() << " = " << x5.domain() << endl;
		cout << '\t' << x6.getName() << " = " << x6.domain() << endl;
		cout << '\t' << x7.getName() << " = " << x7.domain() << endl;
		cout << '\t' << x8.getName() << " = " << x8.domain() << endl;
	}

	RealVar & varX()
	{
		return x1;
	}
	RealVar & varY()
	{
		return x2;
	}
};
